package com.mrx.tunnel.service;

import com.mrx.tunnel.constants.SqlType;
import com.mrx.tunnel.constants.TunnelConstants;
import com.mrx.tunnel.handler.ISqlHandler;
import com.mrx.tunnel.model.Pair;
import com.mrx.tunnel.model.TunnelRequest;
import jakarta.annotation.Nonnull;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.mrx.tunnel.utils.TunnelUtils.*;

/**
 * @author Mr.X
 * @since 2024/8/31 08:22
 */
@Service
public class HttpTunnelService {

    private final Map<SqlType, ISqlHandler> sqlHandlers;

    private static final Logger logger = LoggerFactory.getLogger(HttpTunnelService.class);

    public HttpTunnelService(List<ISqlHandler> sqlHandlers) {
        this.sqlHandlers = sqlHandlers.stream().collect(Collectors.toMap(ISqlHandler::supportType, Function.identity()));
    }

    @SneakyThrows
    public byte[] handleTunnel(TunnelRequest request) {
        if (!request.isRestRequest()) {
            return concatByte(getEchoHeader(TunnelConstants.ERROR_INVALID_STATUS), getBlock("invalid parameters"));
        }
        Pair<SqlType, byte[]> result = checkDBVersion(request.getDbfile(), request.getActn());
        if (result.getRight() != null) {
            return result.getRight();
        }
        return sqlHandlers.get(result.getLeft()).handle(request.getActn(), request.getDbfile(), request.getQueries());
    }

    @Nonnull
    @SneakyThrows
    private Pair<SqlType, byte[]> checkDBVersion(String dbFile, String action) {
        int status = TunnelConstants.ERROR_INVALID_STATUS;
        byte[] header = getEchoHeader(status);
        boolean isCreateAct = TunnelConstants.ACTION_CREATE_2.equals(action) || TunnelConstants.ACTION_CREATE_3.equals(action);
        SqlType sqlType = SqlType.SQLITE_3;
        File file = new File(dbFile);
        try (InputStream stream = Files.newInputStream(file.toPath())) {
            // TODO: 检查文件版本, 记得处理文件无效的情况
            if (isCreateAct) {
                return Pair.ofRight(concatByte(header, getBlock("Database file exists already")));
            }
            return Pair.ofLeft(sqlType);
        } catch (IOException e) {
            if (e instanceof FileNotFoundException) {
                if (isCreateAct) {
                    if (!file.createNewFile()) {
                        return Pair.ofRight(concatByte(header, getBlock("Create database file failed")));
                    }
                    return Pair.ofLeft(sqlType);
                }
                return Pair.ofRight(concatByte(header, getBlock("Database file does not exist")));
            }
            return Pair.ofRight(concatByte(header, getBlock("Database file cannot be opened")));
        }
    }

}
